/**
 * Copyright (c) 2019 Coder League
 * All rights reserved.
 *
 * File：ReleaseHandleService.java
 * History:
 *         2019年7月2日: Initially created, Chrise.
 */
package club.coderleague.ilsp.service.common;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import club.coderleague.ilsp.service.AuthenticationService;
import club.coderleague.ilsp.util.CommonUtil;

/**
 * 发布处理服务。
 * @author Chrise
 */
@Service
public class ReleaseHandleService implements ApplicationContextAware, AuthenticationService {
	private String delimiter;
	private String temporary;
	private ApplicationContext context;
	@Autowired
	private ThreadPoolTaskScheduler scheduler;
	
	private static final String TEMP_DIR_NAME = "temp";
	private static final String LINUX_PATH_DELIMITER = "/";
	private static final String WINDOWS_PATH_DELIMITER = "\\";
	
	/**
	 * 初始化。
	 * @author Chrise 2019年6月28日
	 * @throws Exception 初始化错误。
	 */
	@PostConstruct
	private void initialize() throws Exception {
		File classpath = null;
		
		URL url = ResourceUtils.getURL("classpath:");
		String uri = url.toURI().getPath();
		if (uri != null) classpath = new File(uri);
		else classpath = new File(url.getPath());
		
		if (classpath.getPath().startsWith("file:")) {
			String parent = classpath.getParentFile().getParentFile().getParent().substring(5);
			if (parent.contains(":\\")) {
				parent = parent.substring(1);
				this.delimiter = WINDOWS_PATH_DELIMITER;
			} else this.delimiter = LINUX_PATH_DELIMITER;
			
			this.temporary = parent + this.delimiter + TEMP_DIR_NAME;
		}
	}
	
	/**
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.context = applicationContext;
	}
	
	/**
	 * 验证运行环境。
	 * @author Chrise 2019年6月28日
	 * @return 运行环境支持远程部署返回true，否则返回false。
	 */
	public boolean verify() {
		return (this.temporary != null);
	}
	
	/**
	 * 发布应用程序。
	 * @author Chrise 2019年6月29日
	 * @param request 请求对象。
	 * @return 发布结果，成功时返回空，否则返回错误消息。
	 * @throws IOException 发布包保存错误。
	 */
	public String release(HttpServletRequest request) throws IOException {
		// 验证管理员身份
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		if (!this.getAuthenticationService().authenticate(username, password)) return "身份验证失败";
		
		// 验证发布包文件
		List<MultipartFile> files = ((MultipartHttpServletRequest)request).getFiles("file");
		String name = this.verifyFile(files);
		if (name == null) return "发布包验证失败";
		
		// 保存发布包文件
		String pathname = this.temporary + this.delimiter + name;
		files.get(0).transferTo(new File(pathname));
		
		// 延迟关闭服务
		this.scheduler.schedule(new Runnable() {
			public void run() {
				((ConfigurableApplicationContext)context).close();
			}
		}, new Date(System.currentTimeMillis() + 5000L));
		
		return null;
	}
	
	/**
	 * 获取身份验证服务。
	 * @author Chrise 2019年6月29日
	 * @return 身份验证服务对象。
	 */
	private AuthenticationService getAuthenticationService() {
		Map<String, AuthenticationService> beans = this.context.getBeansOfType(AuthenticationService.class);
		if (beans.isEmpty() || beans.size() > 2) throw new RuntimeException("Authentication service not found or is uncertainty.");
		
		Collection<AuthenticationService> services = beans.values();
		for (AuthenticationService service : services) {
			if (service instanceof ReleaseHandleService) continue;
			return service;
		}
		
		return this;
	}
	
	/**
	 * 验证发布包文件。
	 * @author Chrise 2019年6月29日
	 * @param files 发布包文件集合对象。
	 * @return 发布包文件名。
	 */
	private String verifyFile(List<MultipartFile> files) {
		if (files.isEmpty()) return null;
		
		MultipartFile file = files.get(0);
		String name = file.getOriginalFilename();
		if (CommonUtil.isEmpty(name)) return null;
		
		int index = name.lastIndexOf(this.delimiter);
		if (index != -1) name = name.substring(index + 1);
		if (!name.endsWith(".jar")) return null;
		
		return name;
	}
}
